﻿using System;
using System.Collections.Generic;
using System.Text;

using System.IO;
using System.Threading;
using System.Runtime.Serialization.Formatters.Binary;
using System.Net;

namespace RemoteObject
{
    [Serializable]
    public class DownloadJobTicket
    {
        public Boolean useCompressionMode;
        public String fnLocal;
        public String fnRemote;
        public int fileLength;
        public Common.Status jobStatus;
        public String serverUrl;
    }



    public class DownloadJob : FileTransJob
    {
        private List<FileDisInfoItem> fileDisInfoList;
        private bool isFromMultipleSite;

        public DownloadJob(String fnLocal, String fnRemote, String serverUrl)
        {
            transMode = FileTransMode.Download;
            this.fnLocal = fnLocal;
            this.fnRemote = fnRemote;
            this.serverUrl = serverUrl;
            fileLength = 0;
            jobStatus = Common.Status.Pause;
            isFromMultipleSite = false;
        }

        public DownloadJob(String fnLocal, String fnRemote, List<FileDisInfoItem> fileDisInfoList)
        {
            transMode = FileTransMode.Download;
            this.fnLocal = fnLocal;
            this.fnRemote = fnRemote;
            this.fileDisInfoList = fileDisInfoList;
            fileLength = 0;
            jobStatus = Common.Status.Pause;
            isFromMultipleSite = true;
        }

        public DownloadJob(DownloadJobTicket ticket)
        {
            transMode = FileTransMode.Download;
            this.useCompressionMode = ticket.useCompressionMode;
            this.fnLocal = ticket.fnLocal;
            this.fnRemote = ticket.fnRemote;
            this.jobStatus = ticket.jobStatus;
            this.serverUrl = ticket.serverUrl;
        }

        public DownloadJobTicket GetDownloadJobTicket()
        {
            DownloadJobTicket ticket = new DownloadJobTicket();
            ticket.useCompressionMode = this.useCompressionMode;
            ticket.fnLocal = this.fnLocal;
            ticket.fnRemote = this.fnRemote;
            ticket.fileLength = this.fileLength;
            ticket.jobStatus = this.jobStatus;
            ticket.serverUrl = this.serverUrl;
            return ticket;
        }

        public override void Start()
        {
            base.Start();

            if (jobStatus == Common.Status.Working || jobStatus == Common.Status.Completed) return;
            
            fnNet = fnLocal + Common.DownloadNetFileExtension;

            if (isFromMultipleSite)
            {
                serverUrl = String.Format("tcp://{0}/RemoteObject.RemoteObjectDownloader", fileDisInfoList[0].locatedSite.siteIP ); 
            }
            //check file existence and get file length
            try
            {
                RemoteObject.RemoteObjectDownloader proxyObjectTcp = (RemoteObject.RemoteObjectDownloader)Activator.GetObject(typeof(RemoteObject.RemoteObjectDownloader), serverUrl);
                fileLength = (int)proxyObjectTcp.GetFileLength(fnRemote);
                proxyObjectTcp = null;
                if (fileLength <= 0)
                    throw new Exception();
            }
            catch
            {
                Console.WriteLine("Network error occurs! Download cancelled...");
                return;
            }

            jobStatus = Common.Status.Working;

            //Considering Breakpoint Resuming
            bool isBreakpointResuming = false;
            Random rd = new Random();
            List<Thread> threadList = new List<Thread>();

            if (File.Exists(fnNet) && File.Exists(fnLocal))
            {
                if (remainBlocks.Deserialize(fnNet))
                {
                    // breakpoint resume
                    isBreakpointResuming = true;
                    Console.WriteLine("Break Point Resuming");
                    foreach (BlockInfo bi in remainBlocks)
                    {
                        if (bi.remainDataLength > 0)
                        {
                            if (isFromMultipleSite)
                            {
                                int id = rd.Next(0, fileDisInfoList.Count);
                                serverUrl = String.Format("tcp://{0}/RemoteObject.RemoteObjectDownloader", fileDisInfoList[id].locatedSite.siteIP);
                            }
                            DownloadWorkder downloader = new DownloadWorkder(fnRemote, fnLocal, serverUrl, bi, this);
                            Thread thread = new Thread(new ThreadStart(downloader.Run));
                            threadList.Add(thread);
                        }
                    }
                    StartAllWorkers(threadList);
                }
            }

            if (!isBreakpointResuming)
            {
                // Net Task Begin
                Console.WriteLine("New Task Begin");

                //create the temp local file with full size
                File.Delete(fnLocal);
                FileStream fs = new FileStream(fnLocal, FileMode.CreateNew, FileAccess.ReadWrite, FileShare.ReadWrite);
                fs.SetLength(fileLength);
                fs.Close();

                int maxBlocks = Common.GetMaxBlockNumber(fileLength);
                int blockLength = (int)(fileLength + maxBlocks - 1) / maxBlocks;
                for (int i = 0; i < maxBlocks; i++)
                {
                    int startPos = blockLength * i;
                    int dataLength = Math.Min(blockLength, fileLength - startPos);
                    BlockInfo blockInfo = new BlockInfo(i,startPos,startPos,dataLength);
                    remainBlocks.Add(blockInfo);
                    if (isFromMultipleSite)
                    {
                        int id = rd.Next(0, fileDisInfoList.Count);
                        serverUrl = String.Format("tcp://{0}/RemoteObject.RemoteObjectDownloader", fileDisInfoList[id].locatedSite.siteIP);
                    }
                    DownloadWorkder downloader = new DownloadWorkder(fnRemote, fnLocal, serverUrl, blockInfo, this);
                    Thread thread = new Thread(new ThreadStart(downloader.Run));
                    threadList.Add(thread);
                }
                StartAllWorkers(threadList);
            }
        }

        public override String GetServerURL()
        {
            // TODO: [Vincent] We temporary return a random element of the List.
            // Would include the Alglorithm later.
            if (isFromMultipleSite)
            {
                serverUrl = String.Format("tcp://{0}/RemoteObject.RemoteObjectDownloader", fileDisInfoList[0].locatedSite.siteIP); 
            }
            return serverUrl;
        }

    }
}
